package org.yzhzc.friend.service.user.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.yzhzc.common.core.constants.CacheConstants;
import org.yzhzc.common.core.constants.Constants;
import org.yzhzc.common.core.constants.HttpConstants;
import org.yzhzc.common.core.domain.*;
import org.yzhzc.common.core.domain.vo.LoginUserVO;
import org.yzhzc.common.core.enums.ResultCode;
import org.yzhzc.common.core.enums.UserIdentity;
import org.yzhzc.common.core.enums.UserStatus;
import org.yzhzc.common.core.utils.ThreadLocalUtil;
import org.yzhzc.common.message.service.AliSmsService;
import org.yzhzc.common.redis.service.RedisService;
import org.yzhzc.common.security.exception.ServiceException;
import org.yzhzc.common.security.service.TokenService;
import org.yzhzc.friend.domain.user.dto.*;
import org.yzhzc.friend.domain.user.entity.*;
import org.yzhzc.friend.domain.user.vo.*;
import org.yzhzc.friend.manager.UserCacheManager;
import org.yzhzc.friend.mapper.user.UserMapper;
import org.yzhzc.friend.service.user.IUserService;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
@Slf4j
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private AliSmsService aliSmsService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private UserCacheManager userCacheManager;

    @Value("${sms.code-expiration:5}")
    private Long phoneCodeExpiration;  // 验证码有效时间（分钟）

    @Value("${sms.send-limit:30}")
    private Integer sendLimit;  // 每天最多发送验证码的次数限制

    @Value("${sms.is-send:false}")
    private boolean isSend;  // 是否开启短信发送

    @Value("${jwt.secret}")
    private String secret;  // JWT 密钥

    @Value("${file.oss.downloadUrl:http://null.com}")
    private String downloadUrl;  // 如果配置文件中没有'file.oss.downloadUrl'，则默认值为"http://default-url.com"


    @Override
    public boolean sendCode(UserDTO userDTO) {

        // 验证手机号合法性
        if (!checkPhone(userDTO.getPhone())) {
            throw new ServiceException(ResultCode.FAILED_USER_PHONE);
        }

        // 构造验证码的 Redis 键
        String phoneCodeKey = this.getPhoneCodeKey(userDTO.getPhone());
        // 获取验证码的剩余有效时间
        Long expire = redisService.getExpire(phoneCodeKey, TimeUnit.SECONDS);
        if (expire != null && (phoneCodeExpiration * 60 - expire) < 60) {
            // 如果距离上一次获取验证码的时间小于 60 秒，抛出频繁请求异常
            throw new ServiceException(ResultCode.FAILED_FREQUENT);
        }

        // 获取用户当天已请求验证码的次数（存储在 Redis 中）
        String codeTimeKey = this.getCodeTimeKey(userDTO.getPhone());
        Long sendTimes = redisService.getCacheObject(codeTimeKey, Long.class);
        if (sendTimes != null && sendTimes >= sendLimit) {
            // 如果发送次数已经超过限制，抛出次数限制异常
            throw new ServiceException(ResultCode.FAILED_TIME_LIMIT);
        }

        // 生成验证码，默认使用常量的验证码或随机生成一个验证码
        String code = isSend ? RandomUtil.randomNumbers(6) : Constants.DEFAULT_CODE;
        // 存储验证码到 Redis 中，设置过期时间
        redisService.setCacheObject(phoneCodeKey, code, phoneCodeExpiration, TimeUnit.MINUTES);

        if (isSend) {
            // 如果开启了短信发送，则调用阿里云短信服务发送验证码
            boolean sendMobileCode = aliSmsService.sendMobileCode(userDTO.getPhone(), code);
            if (!sendMobileCode) {
                // 如果发送失败，抛出发送失败的异常
                throw new ServiceException(ResultCode.FAILED_SEND_CODE);
            }
        }

        // 增加验证码请求次数
        redisService.increment(codeTimeKey);
        if (sendTimes == null) {  // 如果是当天第一次请求验证码
            // 设置发送次数的过期时间为当天剩余的秒数
            long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.expire(codeTimeKey, seconds, TimeUnit.SECONDS);
        }

        return true;  // 返回成功
    }

    @Override
    public String codeLogin(String phone, String code) {
        // 校验用户传回的验证码
//        this.checkCode(phone, code);

        // 根据手机号查询用户信息
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));

        if (user == null) {  // 如果用户不存在，则是新用户
            // 新用户注册逻辑
            user = new User();
            user.setPhone(phone);  // 设置手机号
            user.setStatus(UserStatus.Normal.getValue());  // 设置用户状态为正常
            user.setCreateBy(Constants.SYSTEM_USER_ID);  // 设置创建人（系统用户ID）
            userMapper.insert(user);  // 插入新用户数据到数据库
        }

        // 创建并返回用户的认证 token
        return tokenService.createToken(
                user.getUserId(),  // 用户ID
                secret,  // 密钥
                UserIdentity.ORDINARY.getValue(),  // 用户身份类型（普通用户）
                user.getNickName(),  // 用户昵称
                user.getHeadImage()  // 用户头像
        );
    }


    @Override
    public boolean logout(String token) {
        // 判断 token 是否为空且以特定的前缀开头
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            // 去掉 token 前缀
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        // 调用 tokenService 删除登录用户信息
        return tokenService.deleteLoginUser(token, secret);
    }

    @Override
    public R<LoginUserVO> info(String token) {
        // 判断 token 是否为空且以特定的前缀开头
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            // 去掉 token 前缀
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }

        // 调用 tokenService 获取登录用户信息
        LoginUser loginUser = tokenService.getLoginUser(token, secret);

        // 如果登录用户信息为空，返回失败的响应
        if (loginUser == null) {
            return R.fail();
        }

        // 将登录用户信息封装到 LoginUserVO 对象中
        LoginUserVO loginUserVO = new LoginUserVO();
        loginUserVO.setNickName(loginUser.getNickName());  // 设置昵称

        // 如果用户头像存在，拼接完整的头像 URL 并设置
        if (StrUtil.isNotEmpty(loginUser.getHeadImage())) {
            loginUserVO.setHeadImage(downloadUrl + loginUser.getHeadImage());
        }

        // 返回成功响应，包含用户信息
        return R.ok(loginUserVO);
    }


    @Override
    public UserVO detail() {
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        UserVO userVO = userCacheManager.getUserById(userId);
        if (userVO == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        if (StrUtil.isNotEmpty(userVO.getHeadImage())) {
            userVO.setHeadImage(downloadUrl + userVO.getHeadImage());
        }
        return userVO;
    }

    @Override
    public int edit(UserUpdateDTO userUpdateDTO) {
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        User user = userMapper.selectById(userId);
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        user.setNickName(userUpdateDTO.getNickName());
        user.setSex(userUpdateDTO.getSex());
        user.setSchoolName(userUpdateDTO.getSchoolName());
        user.setMajorName(userUpdateDTO.getMajorName());
        user.setPhone(userUpdateDTO.getPhone());
        user.setEmail(userUpdateDTO.getEmail());
        user.setWechat(userUpdateDTO.getWechat());
        user.setIntroduce(userUpdateDTO.getIntroduce());
        //更新用户缓存
        userCacheManager.refreshUser(user);
        tokenService.refreshLoginUser(user.getNickName(),user.getHeadImage(),
                ThreadLocalUtil.get(Constants.USER_KEY, String.class));
        return userMapper.updateById(user);
    }

    @Override
    public int updateHeadImage(String headImage) {
        // 从 ThreadLocal线程局部变量 中获取当前用户的ID
        Long userId = ThreadLocalUtil.get(Constants.USER_ID, Long.class);

        // 如果用户ID为空，抛出用户不存在的异常
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }

        // 根据用户ID从数据库中查询用户信息
        User user = userMapper.selectById(userId);

        // 如果查询到的用户为空，抛出用户不存在的异常
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }

        // 更新用户的头像信息
        user.setHeadImage(headImage);

        // 刷新用户缓存，确保缓存中的用户信息是最新的
        userCacheManager.refreshUser(user);

        // 刷新登录用户的token信息，更新用户的昵称和头像
        tokenService.refreshLoginUser(user.getNickName(), user.getHeadImage(),
                ThreadLocalUtil.get(Constants.USER_KEY, String.class));

        // 将更新后的用户信息保存到数据库，并返回更新操作的结果
        return userMapper.updateById(user);
    }

    // 检验传入的验证码是否匹配
    private void checkCode(String phone, String code) {
        // 根据手机号获取验证码的缓存键
        String phoneCodeKey = this.getPhoneCodeKey(phone);

        // 从Redis缓存中获取验证码
        String cacheCode = redisService.getCacheObject(phoneCodeKey, String.class);

        // 如果缓存中没有验证码，抛出无效验证码的异常
        if (StrUtil.isEmpty(cacheCode)) {
            throw new ServiceException(ResultCode.FAILED_INVALID_CODE);
        }

        // 如果缓存中的验证码与传入的验证码不一致，抛出验证码错误的异常
        if (!cacheCode.equals(code)) {
            throw new ServiceException(ResultCode.FAILED_ERROR_CODE);
        }

        // 验证码比对成功，删除缓存中的验证码
        redisService.deleteObject(phoneCodeKey);
    }

    // 检查手机号是否合法
    public static boolean checkPhone(String phone) {
        // 正则表达式：以1开头，第二位为2-9之间的数字，后面跟着9个数字，共11位
        Pattern regex = Pattern.compile("^1[2|3|4|5|6|7|8|9][0-9]\\d{8}$");
        // 匹配输入的手机号
        Matcher m = regex.matcher(phone);
        // 返回是否匹配成功
        return m.matches();
    }


    // 获取手机号对应的验证码缓存键
    private String getPhoneCodeKey(String phone) {
        return CacheConstants.PHONE_CODE_KEY + phone;
    }

    // 获取手机号对应的验证码时间缓存键
    private String getCodeTimeKey(String phone) {
        return CacheConstants.CODE_TIME_KEY + phone;
    }

}
